apollo serverとclientでファイルをアップロードする方法について調査したのでまとめました。
こちらの公式のドキュメントと、こちらのyoutubeの動画に沿って実装しました。
- https://www.apollographql.com/docs/apollo-server/data/file-uploads/
- https://www.youtube.com/watch?v=BcZ_ItGplfE
実装環境は以下です。
$ node -v
v14.15.4
$ npm -v
7.6.3
ディレクトリ構成
.
├── client
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src
│ │ ├── App.js
│ │ ├── UploadForm.js
│ │ ├── index.css
│ │ └── index.js
│ └── yarn.lock
└── fileupload-server
├── package-lock.json
├── package.json
├── public
│ └── images
└── server.js
clientはreactです。 fileupload-server/public/images/にアップロードした画像が保存されます。
serverサイド
まずserver側から初期化を行なってから実装していきます。
$ mkdir fileupload-server
$ cd fileupload-server/
$ npm init -y
$ npm install apollo-server
$
server.js
const { ApolloServer, gql } = require('apollo-server');
const path = require('path')
const fs = require('fs')
const typeDefs = gql`
type File {
url: String!
}
type Query {
hello: String!
}
type Mutation {
uploadFile(file: Upload!): File!
}
`;
const resolvers = {
Query: {
hello: () => 'Hello World',
},
Mutation: {
uploadFile: async (parent, { file }) => {
const { createReadStream, filename, mimetype, encording } = await file
const stream = createReadStream()
const pathName = path.join(__dirname, `/public/images/${filename}`)
await stream.pipe(fs.createWriteStream(pathName))
return {
url: `http://localhost:4000/images/${filename}`
}
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
front側
front側も初期化から
$ cd ..
$ npx create-react-app client --template clean-cra
$ npm install @apollo/client graphql apollo-upload-client
App.js
import React from 'react';
import { ApolloProvider, ApolloClient, InMemoryCache} from '@apollo/client'
import { createUploadLink} from 'apollo-upload-client'
import UploadForm from './UploadForm'
const clint = new ApolloClient({
link: createUploadLink({
uri: 'http://localhost:4000/'
}),
cache: new InMemoryCache(),
})
function App() {
return (
<ApolloProvider client={clint}>
<UploadForm>
</UploadForm>
</ApolloProvider>
);
}
export default App;
UploadForm.js
import React from 'react'
import { useMutation, gql } from '@apollo/client'
const UPLOAD_FILE = gql`
mutation uploadFile($file: Upload!) {
uploadFile(file: $file) {
url
}
}
`
export default function UploadForm() {
const [uploadFile] = useMutation(UPLOAD_FILE, {
onCompleted: data => console.log(data)
})
const handleFileChange = e => {
const file = e.target.files[0]
if (!file) return
uploadFile({
variables: {file}
})
}
return (
<div>
<h1>Upload File</h1>
<input type="file" onChange={handleFileChange} />
</div>
)
}
サーバ起動
$ node server.js
サーバ側動作確認
ここにアクセスする。 http://localhost:4000/
表示されたPlaygrondで以下のように入力して実行すると Hello World
と表示されます。
query {
hello
}
では次はファイルアップロードの確認
フロント側起動
$ npm run start
アクセスする。 http://localhost:3000/
ファイルを選択してアップロードするとこうなる
サーバ側のpublic/images/に選択したファイルが保存される。